package com.partsoft.webs.spring.zks;


import java.lang.reflect.Field;
import java.util.List;

import javax.servlet.ServletContext;

import org.springframework.context.ApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.WebApp;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventThreadCleanup;
import org.zkoss.zk.ui.event.EventThreadInit;
import org.zkoss.zk.ui.event.EventThreadResume;

import com.partsoft.webs.ContextUtils;


/**
 * 事件线程上下文设置接口
 * 注：由于ZK采用线程处理事件，因此在每个事件处理线程中，必须先进行设置上下文！
 */
@SuppressWarnings({"unchecked",  "rawtypes"})
public class ThreadContextInterceptor implements EventThreadInit,
		EventThreadCleanup, EventThreadResume {

	private static ThreadLocal<ApplicationContext> lc_oldAppCtx = new ThreadLocal<ApplicationContext>();
	
	private boolean threadEnabled;
	
	private ServletContext servletContext;
	
	public ThreadContextInterceptor() {
		final WebApp app = Executions.getCurrent().getDesktop().getWebApp();
		this.servletContext = (ServletContext) app.getServletContext();
		if ( app == null  || this.servletContext == null) {
			throw new IllegalStateException("必须运行在ZK Web环境下");
		}
		//是否启用事件线程
		threadEnabled = app.getConfiguration().isEventThreadEnabled();
	}
	
	protected WebApplicationContext findWebApplicationContext() {
		return WebApplicationContextUtils.getWebApplicationContext(this.servletContext);
	}
	
	/**
	 * 运行在Servlet线程中(事件处理之前)
	 */
	public void prepare(Component comp, Event event) throws Exception {}

	/**
	 * 运行在事件处理线程中（事件处理之前）
	 */
	public boolean init(Component comp, Event event) throws Exception {
		setupEventThreadContext();
		return true;
	}

	/**
	 * 运行在事件处理线程中（事件处理之后）
	 */
	public void cleanup(Component comp, Event evt, List errs) throws Exception {
		cleanEventThreadContext();
	}

	/**
	 * 运行在Servlet处理线程中(事件处理完成之后)
	 */
	public void complete(Component comp, Event evt) throws Exception {}

	/**
	 * 运行在事件处理线程中（线程恢复中段，不会调用任何{@link #afterResume(Component, Event)},<br/>
	 * {@link #cleanup(Component, Event, List)},{@link #complete(Component, Event)}方法)<br/>
	 * 此方法运行在主线程中（Servlet线程）
	 */
	public void abortResume(Component comp, Event evt) throws Exception {
		cleanEventThreadContext();
	}

	/**
	 * 恢复运行之后(事件处理线程中)
	 */
	public void afterResume(Component comp, Event evt) throws Exception {
		cleanEventThreadContext();
	}

	/**
	 * 恢复运行之前(Servlet处理线程中)
	 */
	public void beforeResume(Component comp, Event evt) throws Exception {}
	
	protected void setupEventThreadContext() {
		ApplicationContext ctx  = findWebApplicationContext();
		ThreadLocal<ApplicationContext> tlc = null;
		ApplicationContext oldAppCtx = null;
		if (ctx != null) {
			Field clf = null;
			try {
				clf = ContextUtils.class.getDeclaredField("contextLocal");
			} catch (Exception e) {
				throw new IllegalStateException(e.getMessage(), e);
			}
			boolean oldAcs = clf.isAccessible();
			clf.setAccessible(true);
			try {
				tlc = (ThreadLocal<ApplicationContext>) clf.get(null);
			} catch (Exception e) {
				throw new IllegalStateException(e.getMessage(), e);
			} finally { 
				clf.setAccessible(oldAcs);
			}
			if (tlc != null) {
				oldAppCtx = tlc.get();
				tlc.set(ctx);
			}
		}
		lc_oldAppCtx.set(oldAppCtx);
	}
	
	protected void cleanEventThreadContext() {
		ThreadLocal<ApplicationContext> tlc = null;
		Field clf = null;
		try {
			clf = ContextUtils.class.getDeclaredField("contextLocal");
		} catch (Exception e) {
			throw new IllegalStateException(e.getMessage(), e);
		}
		boolean oldAcs = clf.isAccessible();
		clf.setAccessible(true);
		try {
			tlc = (ThreadLocal<ApplicationContext>) clf.get(null);
		} catch (Exception e) {
			throw new IllegalStateException(e.getMessage(), e);
		} finally { 
			clf.setAccessible(oldAcs);
		}
	
		if (tlc != null) {
			tlc.set(lc_oldAppCtx.get());
		}
		lc_oldAppCtx.set(null);
	}
	
	public boolean isThreadEnabled() {
		return threadEnabled;
	}
}
